package com.yznote.cloudesk.file.server.impl;

import com.yznote.cloudesk.common.constants.YCacheConstant;
import com.yznote.cloudesk.common.constants.YConstant;
import com.yznote.cloudesk.common.exception.FileNameRepeatException;
import com.yznote.cloudesk.common.pojo.DeskFile;
import com.yznote.cloudesk.common.pojo.File;
import com.yznote.cloudesk.common.tokenShare.YTokenShare;
import com.yznote.cloudesk.common.utils.DeskFile2FileUtils;
import com.yznote.cloudesk.common.utils.IdUtils;
import com.yznote.cloudesk.common.oss.OssUtils;
import com.yznote.cloudesk.cache.yCache.YCache;
import com.yznote.cloudesk.file.mapper.DeskMapper;
import com.yznote.cloudesk.file.mapper.FileMapper;
import com.yznote.cloudesk.file.server.FileServer;
import com.yznote.cloudesk.file.server.GarbageServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Service
public class FileServerImpl implements FileServer {
    @Autowired
    DeskMapper deskMapper;
    @Autowired
    FileMapper fileMapper;
    @Autowired
    OssUtils ossUtils;
    @Autowired
    YCache yCache;
    @Autowired
    YTokenShare yTokenShare;
    @Autowired
    GarbageServer garbageServer;

    @Override
    public List<DeskFile> getDesk(Long userId) {
        List<DeskFile> deskFileList = deskMapper.getDeskFileList(userId);
        return deskFileList;
    }

    @Override
    public List<File> getSonFile(Long fileId, Long userId) {
        List<File> sonFile = fileMapper.getSonFile(fileId, userId);
        return sonFile;
    }

    @Override
    public File getFileByFaIdAndFileName(Long fileFaId, String fileName) {
        return fileMapper.getFileByFaIdAndFileName(yTokenShare.get().getUserId(), fileName, fileFaId);
    }

    @Override
    @Transactional
    public String insertFile(DeskFile deskFile, Boolean isText, String afterName) {
        if (afterName == null) {
            afterName = "";
        }
        Long fileFaId = deskFile.getFileFaId();
        if (fileFaId == null || fileFaId.equals(0L)) {
            return YConstant.ERROR_FILEFAIDISNULL;
        }
        Long userId = yTokenShare.get().getUserId();
        List<File> currFile = getSonFile(fileFaId, userId);
        //后端命名
        if (deskFile.getFileName() == null) {
            String fileName = autoFileName(currFile, isText ? "新建文件" : "新建文件夹", afterName);
            deskFile.setFileName(fileName);
        } else {
            //自命名防止重复
            File oldFile = fileMapper.getFileByFaIdAndFileName(userId, deskFile.getFileName() + afterName, fileFaId);
            if (oldFile != null) {
                throw new FileNameRepeatException(YConstant.ERROR_FILENAMEREPEAT, 501, oldFile.getFileId());
            }
            deskFile.setFileName(deskFile.getFileName() + afterName);
        }
        deskFile.setFileId(IdUtils.nextId());
        deskFile.setUserId(userId);
        if (isText) {
            deskFile.setFileType(1L);
            deskFile.setFilePath("filesystem/" + deskFile.getUserId() + "/" + deskFile.getFileId());
        } else {
            deskFile.setFileType(0L);
        }
        File file = DeskFile2FileUtils.deskFile2File(deskFile);
        file.setCreateBy(userId);
        file.setCreateTime(LocalDateTime.now());
        //新建在桌面存桌面和普通文件
        if (deskFile.getFileFaId().equals(userId)) {
            if (deskFile.getX() == null ^ deskFile.getY() == null) {
                return YConstant.ERROR_DATAEXC + "x、y异常";
            }
            //自动给x y轴
            if (deskFile.getX() == null && deskFile.getY() == null) {
                int[] xy = autoXY(userId);
                if (xy[0] == -1) {
                    return "[error]桌面已满";
                }
                deskFile.setX(xy[0]);
                deskFile.setY(xy[1]);
            }
            try {
                deskMapper.insertFile(deskFile);
            } catch (Exception e) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                yCache.delayDelete(YCacheConstant.DESK, yTokenShare.get().getUserId().toString());
                return YConstant.ERROR_DATAEXC;
            }
            //新增桌面文件删除缓存
            yCache.delayDelete(YCacheConstant.DESK, yTokenShare.get().getUserId().toString());
        }
        //新建在其他地方存普通文件
        fileMapper.insertFilemsg(file);
        return YConstant.SUCCESS;
    }

    @Override
    @Transactional
    public String insertFile(DeskFile deskFile, String text, String afterName) throws UnsupportedEncodingException {
        String msg = insertFile(deskFile, true, afterName);
        if (msg != YConstant.SUCCESS) {
            return msg;
        }
        ossUtils.pushFileSource(text, deskFile.getFilePath());
        return YConstant.SUCCESS;
    }

    @Override
    @Transactional
    public String insertFile(DeskFile deskFile, MultipartFile mtf) throws IOException {
        byte[] bytes = mtf.getBytes();
        deskFile.setMemory((long) bytes.length);
        String msg = insertFile(deskFile, true, null);
        if (msg == YConstant.SUCCESS) {
            ossUtils.pushFileSource(bytes, fileMapper.getFilemsg(deskFile.getFileId()).getFilePath());
        }
        return msg;
    }

    @Override
    @Transactional
    public String removeRepeat(Long oldFileId, DeskFile deskFile, MultipartFile mtf, Boolean isCoverage, String afterName) throws IOException {
        Long userId = yTokenShare.get().getUserId();
        LocalDateTime nowTime = LocalDateTime.now();
        String msg = YConstant.SUCCESS;
        File oldFilemsg = fileMapper.getFilemsg(oldFileId);
        //文件的处理
        if (deskFile.getFileType().equals(1L)) {
            //覆盖
            if (isCoverage) {
                //都是文件就直接覆盖
                if (oldFilemsg.getFileType() == 1) {
                    byte[] bytes;
                    if (mtf == null) {
                        bytes = "".getBytes();
                        deskFile.setMemory(0L);
                    } else {
                        bytes = mtf.getBytes();
                        deskFile.setMemory((long) bytes.length);
                    }
                    File file = DeskFile2FileUtils.deskFile2File(deskFile);
                    file.setUpdateBy(userId);
                    file.setUpdateTime(nowTime);
                    fileMapper.updateFileMemory(file);
                    deskFile.setFileId(oldFileId);
                    ossUtils.pushFileSource(bytes, oldFilemsg.getFilePath());
                    yCache.putByteArray(YCacheConstant.FILE, oldFilemsg.getFilePath(), bytes);
                } else {
                    //否则删除旧目录并新增文件
                    garbageServer.insertMoreGarbages(oldFileId);
                    deskFile.setFileName(deskFile.getFileName() + afterName);
                    msg = insertFile(deskFile, mtf);
                }
                //非覆盖处理
            } else {
                List<File> sonFile = fileMapper.getSonFile(deskFile.getFileFaId(), userId);
                String fileName = autoFileName(sonFile, deskFile.getFileName(), afterName);
                deskFile.setFileName(fileName);
                msg = insertFile(deskFile, mtf);
            }
        } else {
            if (isCoverage) {
                //删除原来文件夹
                garbageServer.insertMoreGarbages(oldFileId);
                deskFile.setFileName(deskFile.getFileName() + afterName);
                msg = insertFile(deskFile, false, null);
            } else {
                List<File> sonFile = fileMapper.getSonFile(deskFile.getFileFaId(), userId);
                String fileName = autoFileName(sonFile, deskFile.getFileName(), afterName);
                deskFile.setFileName(fileName);
                msg = insertFile(deskFile, false, null);
            }
        }
        return msg;
    }

    @Override
    public String moveDeskFile(Long fileId, Integer x, Integer y) {
        deskMapper.moveFile(fileId, x, y);
        return YConstant.SUCCESS;
    }


    @Override
    @Transactional
    public String swapDeskFile(DeskFile deskFile1, DeskFile deskFile2) {
        deskMapper.moveFile(deskFile1.getFileId(), 100, 100);
        deskMapper.moveFile(deskFile2.getFileId(), deskFile1.getX(), deskFile1.getY());
        deskMapper.moveFile(deskFile1.getFileId(), deskFile2.getX(), deskFile2.getY());
        return YConstant.SUCCESS;
    }

    @Override
    public String getFilePath(Long fileId) {
        return fileMapper.getFilePath(fileId);
    }

    @Override
    public String updateFile(File file) {
        Long userId = yTokenShare.get().getUserId();
        Long fileFaId = file.getFileFaId();
        if (fileFaId == null) {
            fileFaId = fileMapper.getFilemsg(file.getFileId()).getFileFaId();
        }
        File oldFile = fileMapper.getFileByFaIdAndFileName(userId, file.getFileName(), fileFaId);
        if (oldFile != null) {
            throw new FileNameRepeatException(YConstant.ERROR_FILENAMEREPEAT, 501, oldFile.getFileId());
        }
        file.setUpdateTime(LocalDateTime.now());
        file.setUpdateBy(yTokenShare.get().getUserId());
        fileMapper.updateFilemsg(file);
        return YConstant.SUCCESS;

    }

    @Override
    public String updateFileName(File file) {
        Long userId = yTokenShare.get().getUserId();
        File file1 = fileMapper.getFilemsg(file.getFileId());
        if (file1.getFileId().equals(file.getFileId()) && file1.getFileName().equals(file.getFileName())) {
            return YConstant.SUCCESS;
        }
        Long fileFaId = file.getFileFaId();
        if (fileFaId == null) {
            fileFaId = file1.getFileFaId();
        }
        File oldFile = fileMapper.getFileByFaIdAndFileName(userId, file.getFileName(), fileFaId);
        if (oldFile != null) {
            throw new FileNameRepeatException(YConstant.ERROR_FILENAMEREPEAT, 501, oldFile.getFileId());
        }
        file.setUpdateTime(LocalDateTime.now());
        file.setUpdateBy(yTokenShare.get().getUserId());
        fileMapper.updateFileName(file);
        return YConstant.SUCCESS;
    }

    @Override
    @Transactional
    public String deskFileToFile(Long fileId, Long newFileFaId) {
        File filemsg = fileMapper.getFilemsg(fileId);
        Long userId = yTokenShare.get().getUserId();
        //判断目的地址是否有相同名字
        File oldFile = fileMapper.getFileByFaIdAndFileName(userId, filemsg.getFileName(), newFileFaId);
        if (oldFile != null) {
            throw new FileNameRepeatException(YConstant.ERROR_FILENAMEREPEAT, 501, oldFile.getFileId());
        }
        //删除桌面文件信息
        deskMapper.DeleteFile(fileId);
        //改变父id
        fileMapper.moveFile(fileId, newFileFaId, fileMapper.getFilemsg(newFileFaId).getFileLevel() + 1);
        return YConstant.SUCCESS;
    }

    @Override
    public String toDeskFile(Long fileId, Integer x, Integer y) {
        File filemsg = fileMapper.getFilemsg(fileId);
        Long userDeskId = yTokenShare.get().getUserId();
        //判断桌面是否有相同名字
        File oldFile = fileMapper.getFileByFaIdAndFileName(userDeskId, filemsg.getFileName(), userDeskId);
        if (oldFile != null) {
            throw new FileNameRepeatException(YConstant.ERROR_FILENAMEREPEAT, 501, oldFile.getFileId());
        }
        //新增桌面文件信息
        deskMapper.insertFile(DeskFile2FileUtils.File2deskFile(filemsg, x, y));
        fileMapper.moveFile(fileId, userDeskId, 2);
        return YConstant.SUCCESS;
    }

    @Override
    public Boolean sortDesk(Long userId) {
        List<DeskFile> desk = getDesk(userId);
        // 默认按照文件名进行排序
        Collections.sort(desk, (e1, e2) -> e1.getFileName().compareTo(e2.getFileName()));
        // 排序完毕 按照x,y 进行重新设置
        // 所有文件腾出空间
        int x = 1000;
        int y = 0;
        for (int i = 0; i < desk.size(); i++) {
            DeskFile file = desk.get(i);
            deskMapper.moveFile(file.getFileId(), x, y++);
        }
        x = 0;
        y = 0;
        for (int i = 0; i < desk.size(); i++) {
            DeskFile file = desk.get(i);
            deskMapper.moveFile(file.getFileId(), x, y++);
            x += y / 12;
            y = y % 12;
        }
        return true;
    }

    /**
     * 命名方法
     *
     * @param currFile
     * @param firstName
     * @param afterName
     * @return String name
     */
    public String autoFileName(List<File> currFile, String firstName, String afterName) {
        Set<String> nameSet = new HashSet();
        for (File file : currFile) {
            nameSet.add(file.getFileName());
        }
        if (nameSet.add(firstName + afterName)) {
            return firstName + afterName;
        }
        int i = 1;
        String temp = firstName + "(";
        while (true) {
            String s = temp + i + ")" + afterName;
            if (nameSet.add(s)) {
                return s;
            }
            i++;
        }
    }

    /**
     * 自动设置x y
     *
     * @param userId
     * @return
     */
    public int[] autoXY(Long userId) {
        List<DeskFile> deskList = getDesk(userId);
        boolean[][] arr = new boolean[4][12];
        for (DeskFile deskFile : deskList) {
            arr[deskFile.getX()][deskFile.getY()] = true;
        }
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[0].length; j++) {
                if (arr[i][j] == false) {
                    return new int[]{i, j};
                }
            }
        }
        return new int[]{-1, -1};
    }


//    public void updateFaFileTime(File file){
//        file.
//    }
}
